package com.richmond.gui.bilan;

import com.richmond.data.bilan.BilanRecord;
import com.richmond.data.bilan.Category;
import com.richmond.data.bilan.CategoryRecords;
import com.richmond.data.bilan.Gl;
import com.richmond.util.BilanParser;
import com.richmond.util.JavaFxUtil;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.util.Callback;
import org.apache.log4j.Logger;

import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.*;
import java.util.prefs.Preferences;

/**
 * User: csavard
 * Date: 12-08-07
 * Time: 17:36
 */
public class BilanController implements Initializable {
    private static final String LIGHT_ROW = "light-row";
    private static final String DARK_ROW = "dark-row";
    private static final String TOTAL = "total";
    private static final String NORMAL = "normal";
    private Logger logger = Logger.getLogger(BilanController.class);
    private Map<Long, String> styleMap;

    @FXML //  fx:id="aoutCol"
    private TableColumn<BilanRecord, Double> aoutCol; // Value injected by FXMLLoader

    @FXML //  fx:id="avrilCol"
    private TableColumn<BilanRecord, Double> avrilCol; // Value injected by FXMLLoader

    @FXML //  fx:id="excelRowCol"
    private TableColumn<BilanRecord, Long> excelRowCol; // Value injected by FXMLLoader

    @FXML //  fx:id="codeCol"
    private TableColumn<BilanRecord, Long> codeCol; // Value injected by FXMLLoader

    @FXML //  fx:id="compteCol"
    private TableColumn<BilanRecord, Gl> glCol; // Value injected by FXMLLoader

    @FXML //  fx:id="compteCol"
    private TableColumn<BilanRecord, String> compteCol; // Value injected by FXMLLoader


    @FXML //  fx:id="decembreCol"
    private TableColumn<BilanRecord, Double> decembreCol; // Value injected by FXMLLoader

    @FXML //  fx:id="descriptionCol"
    private TableColumn<BilanRecord, String> descriptionCol; // Value injected by FXMLLoader

    @FXML //  fx:id="fevrierCol"
    private TableColumn<BilanRecord, Double> fevrierCol; // Value injected by FXMLLoader

    @FXML //  fx:id="janvierCol"
    private TableColumn<BilanRecord, Double> janvierCol; // Value injected by FXMLLoader

    @FXML //  fx:id="juilletCol"
    private TableColumn<BilanRecord, Double> juilletCol; // Value injected by FXMLLoader

    @FXML //  fx:id="juinCol"
    private TableColumn<BilanRecord, Double> juinCol; // Value injected by FXMLLoader

    @FXML //  fx:id="list"
    private ListView<CategoryRecords> list; // Value injected by FXMLLoader

    @FXML //  fx:id="maiCol"
    private TableColumn<BilanRecord, Double> maiCol; // Value injected by FXMLLoader

    @FXML //  fx:id="marsCol"
    private TableColumn<BilanRecord, Double> marsCol; // Value injected by FXMLLoader

    @FXML //  fx:id="novembreCol"
    private TableColumn<BilanRecord, Double> novembreCol; // Value injected by FXMLLoader

    @FXML //  fx:id="octobreCol"
    private TableColumn<BilanRecord, Double> octobreCol; // Value injected by FXMLLoader

    @FXML //  fx:id="root"
    private AnchorPane root; // Value injected by FXMLLoader

    @FXML //  fx:id="septembreCol"
    private TableColumn<BilanRecord, Double> septembreCol; // Value injected by FXMLLoader

    @FXML //  fx:id="totalCol"
    private TableColumn<BilanRecord, Double> totalCol; // Value injected by FXMLLoader

    @FXML //  fx:id="table"
    private TableView<BilanRecord> table; // Value injected by FXMLLoader

    @FXML //  fx:id="selectedCategorie"
    private Text selectedCategorie; // Value injected by FXMLLoader

    private CategoryRecords categoryRecords;


    public void open(ActionEvent event) {
        String initialDirectory = Preferences.userRoot().get("BilanInitialDirectory", null);
        FileChooser fileChooser = new FileChooser();
        if (initialDirectory != null) {
            fileChooser.setInitialDirectory(new File(initialDirectory));
        }

        final File file = fileChooser.showOpenDialog(root.getScene().getWindow());
        if (file != null) {
            Task task = new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    try {
                        updateMessage("Ouverture du fichier \n" + file.getName() + "...");

                        Platform.runLater(new Runnable() {
                            @Override
                            public void run() {
                                list.getItems().clear();
                            }
                        });

                        Preferences.userRoot().put("BilanInitialDirectory", file.getParentFile().getAbsolutePath());
                        logger.info("File: " + file.getAbsolutePath());

                        final Map<Category, CategoryRecords> records = BilanParser.parse(new FileInputStream(file));
                        for (final CategoryRecords bilan : records.values()) {
                            Platform.runLater(new Runnable() {
                                @Override
                                public void run() {
                                    list.getItems().add(bilan);
                                }
                            });

                            Thread.sleep(15);
                        }

                        updateMessage("Traitement terminé");
                        Thread.sleep(100);
                        Platform.runLater(new Runnable() {
                            @Override
                            public void run() {
                                list.getSelectionModel().select(0);
                            }
                        });

                    } catch (Exception e) {
                        logger.error(null, e);
                    }
                    return null;
                }
            };

            JavaFxUtil.runBackgroundTask(task, root);
        }
    }

    @Override
    public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
        assert aoutCol != null : "fx:id=\"aoutCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert avrilCol != null : "fx:id=\"avrilCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert compteCol != null : "fx:id=\"compteCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert excelRowCol != null : "fx:id=\"excelRowCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert codeCol != null : "fx:id=\"codeCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert glCol != null : "fx:id=\"glCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert decembreCol != null : "fx:id=\"decembreCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert descriptionCol != null : "fx:id=\"descriptionCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert fevrierCol != null : "fx:id=\"fevrierCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert janvierCol != null : "fx:id=\"janvierCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert juilletCol != null : "fx:id=\"juilletCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert juinCol != null : "fx:id=\"juinCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert list != null : "fx:id=\"list\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert maiCol != null : "fx:id=\"maiCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert marsCol != null : "fx:id=\"marsCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert novembreCol != null : "fx:id=\"novembreCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert octobreCol != null : "fx:id=\"octobreCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert root != null : "fx:id=\"root\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert septembreCol != null : "fx:id=\"septembreCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert totalCol != null : "fx:id=\"totalCol\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert table != null : "fx:id=\"table\" was not injected: check your FXML file 'BilanForm.fxml'.";
        assert selectedCategorie != null : "fx:id=\"selectedCategorie\" was not injected: check your FXML file 'BilanForm.fxml'.";

        selectedCategorie.setText(null);

        list.getSelectionModel().selectedItemProperty().addListener(new CategoryChangeListener());

        BlankZeroFormatCellFactory blankZeroFormatCellFactory = new BlankZeroFormatCellFactory();
        MoneyFormatCellFactory moneyFormatCellFactory = new MoneyFormatCellFactory();

        compteCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, String>("compte"));
        excelRowCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Long>("excelRow"));
        excelRowCol.setCellFactory(blankZeroFormatCellFactory);
        codeCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Long>("code"));
        codeCol.setCellFactory(blankZeroFormatCellFactory);
        descriptionCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, String>("description"));
        glCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Gl>("gl"));
        janvierCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("janvier"));
        janvierCol.setCellFactory(moneyFormatCellFactory);
        fevrierCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("fevrier"));
        fevrierCol.setCellFactory(moneyFormatCellFactory);
        marsCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("mars"));
        marsCol.setCellFactory(moneyFormatCellFactory);
        avrilCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("avril"));
        avrilCol.setCellFactory(moneyFormatCellFactory);
        maiCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("mai"));
        maiCol.setCellFactory(moneyFormatCellFactory);
        juinCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("juin"));
        juinCol.setCellFactory(moneyFormatCellFactory);
        juilletCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("juillet"));
        juilletCol.setCellFactory(moneyFormatCellFactory);
        aoutCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("aout"));
        aoutCol.setCellFactory(moneyFormatCellFactory);
        septembreCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("septembre"));
        septembreCol.setCellFactory(moneyFormatCellFactory);
        octobreCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("octobre"));
        octobreCol.setCellFactory(moneyFormatCellFactory);
        novembreCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("novembre"));
        novembreCol.setCellFactory(moneyFormatCellFactory);
        decembreCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("decembre"));
        decembreCol.setCellFactory(moneyFormatCellFactory);
        totalCol.setCellValueFactory(new PropertyValueFactory<BilanRecord, Double>("total"));
        totalCol.setCellFactory(new TotalMoneyFormatCellFactory());

        styleMap = new LinkedHashMap<>();
        table.setRowFactory(new TableViewTableRowCallback());
    }

    private static class MoneyFormatCellFactory implements Callback<TableColumn<BilanRecord, Double>, TableCell<BilanRecord, Double>> {
        private final com.richmond.gui.bilan.MoneyFormatCellFactory moneyFormatCellFactory = new com.richmond.gui.bilan.MoneyFormatCellFactory();

        @Override
        public TableCell<BilanRecord, Double> call(TableColumn<BilanRecord, Double> bilanRecordDoubleTableColumn) {
            return moneyFormatCellFactory.call(bilanRecordDoubleTableColumn);
        }
    }


    private class CategoryChangeListener implements ChangeListener<CategoryRecords> {
        @Override
        public void changed(ObservableValue<? extends CategoryRecords> observableValue, CategoryRecords record, final CategoryRecords record1) {
            categoryRecords = record1;
            table.getItems().clear();
            selectedCategorie.setText(null);
            if (record1 != null) {
                selectedCategorie.setText(categoryRecords.toString());
                if (categoryRecords.getRecords() != null) {
                    styleMap.clear();

                    Collection<BilanRecord> records = new Vector<>(categoryRecords.getRecords());
                    BilanRecord total = new BilanRecord();
                    total.setDescription("Total");
                    total.setTotalRecord(true);

                    for (BilanRecord bilanRecord : records) {
                        styleMap.put(bilanRecord.getCode(), (String) null);

                        total.setJanvier(total.getJanvier() + bilanRecord.getJanvier());
                        total.setFevrier(total.getFevrier() + bilanRecord.getFevrier());
                        total.setMars(total.getMars() + bilanRecord.getMars());
                        total.setAvril(total.getAvril() + bilanRecord.getAvril());
                        total.setMai(total.getMai() + bilanRecord.getMai());
                        total.setJuin(total.getJuin() + bilanRecord.getJuin());
                        total.setJuillet(total.getJuillet() + bilanRecord.getJuillet());
                        total.setAout(total.getAout() + bilanRecord.getAout());
                        total.setSeptembre(total.getSeptembre() + bilanRecord.getSeptembre());
                        total.setOctobre(total.getOctobre() + bilanRecord.getOctobre());
                        total.setNovembre(total.getNovembre() + bilanRecord.getNovembre());
                        total.setDecembre(total.getDecembre() + bilanRecord.getDecembre());
                    }

                    records.add(total);
                    styleMap.put(total.getCode(), (String) null);

                    Set<Long> codes = styleMap.keySet();
                    int index = 0;
                    for (Long code : codes) {
                        if (index % 2 == 0) {
                            styleMap.put(code, LIGHT_ROW);
                        } else {
                            styleMap.put(code, DARK_ROW);
                        }
                        index++;
                    }
                    table.setItems(FXCollections.observableArrayList(records));
                }
            }
        }
    }

    private class TableViewTableRowCallback implements Callback<TableView<BilanRecord>, TableRow<BilanRecord>> {

        @Override
        public TableRow<BilanRecord> call(TableView<BilanRecord> bilanRecordTableView) {
            TableRow<BilanRecord> row = new BilanRecordTableRow();
            return row;
        }

        private class BilanRecordTableRow extends TableRow<BilanRecord> {
            @Override
            protected void updateItem(BilanRecord record, boolean b) {
                super.updateItem(record, b);
                if (record != null) {
                    ObservableList<String> styleClass = getStyleClass();
                    Iterator<String> iterator = styleClass.iterator();
                    while (iterator.hasNext()) {
                        String style = iterator.next();
                        if (style.equals(LIGHT_ROW) || style.equals(DARK_ROW) || style.equals(TOTAL) || style.equals(NORMAL)) {
                            iterator.remove();
                        }
                    }

                    String style = styleMap.get(record.getCode());
                    if (style != null) {
                        styleClass.add(style);
                    }

                    if (record.isTotalRecord()) {
                        styleClass.add(TOTAL);
                    } else {
                        styleClass.add(NORMAL);
                    }
                }
            }
        }
    }
}
